import usePortal from "../use/usePortal";
import usezIndex from "../use/usezIndex";
import { PropType, Teleport, computed, createApp, createVNode, defineComponent, ref, watch, watchEffect } from "vue";
import { createUniqueId } from "../use/createUniqueId";
import Draggable from "../Draggable";
import Button, { ButtonProps } from "../Button";
import { FeatherX } from "cui-vue-icons/feather";
import { F7CheckmarkAltCircleFill, F7ExclamationmarkTriangleFill, F7InfoCircleFill, F7QuestionCircleFill, F7XmarkCircleFill } from "cui-vue-icons/f7";
import useTransition from "../use/useTransition";

const icons: any = {
    info: F7InfoCircleFill,
    success: F7CheckmarkAltCircleFill,
    warning: F7ExclamationmarkTriangleFill,
    error: F7XmarkCircleFill,
    confirm: F7QuestionCircleFill
};

type Position = {
    top?: string,
    bottom?: string,
    left?: string,
    right?: string,
}

type ModalProps = {
    bounds?: string,
    disabled?: boolean,
    style?: any,
    class?: any,
    headerStyle?: any,
    bodyStyle?: any,
    title?: any,
    content?: any,
    footer?: boolean,
    footerAlign?: 'start' | 'center' | 'end',
    footerReverse?: boolean,
    loading?: boolean,
    onOk?: () => boolean | Promise<boolean | void> | undefined | void,
    onCancel?: () => void,
    onClosed?: () => void,
    onClickClose?: () => void,
    okText?: any,
    okButtonType?: ButtonProps['type'],
    cancleButtonType?: ButtonProps['type'],
    cancleText?: any,
    modelValue?: boolean,
    defaultPosition?: Position,
    mask?: boolean,
    maskClosable?: boolean,
    resetPostion?: boolean,
    hasCloseIcon?: boolean
    fullScreen?: boolean
    destroyOnClose?: boolean
}

const Modal = defineComponent({
    name: 'Modal',
    props: {
        bounds: {type: String as PropType<ModalProps['bounds']>},
        disabled: {type: Boolean as PropType<ModalProps['disabled']>},
        style: {type: Object as PropType<ModalProps['style']>},
        class: {type: String as PropType<ModalProps['class']>},
        headerStyle: {type: Object as PropType<ModalProps['headerStyle']>},
        bodyStyle: {type: Object as PropType<ModalProps['bodyStyle']>},
        title: {type: [String, Object] as PropType<ModalProps['title']>},
        content: {type: [String, Object] as PropType<ModalProps['content']>},
        footer: {type: Boolean as PropType<ModalProps['footer']>, default: true},
        footerAlign: {type: String as PropType<ModalProps['footerAlign']>},
        footerReverse: {type: Boolean as PropType<ModalProps['footerReverse']>},
        loading: {type: Boolean as PropType<ModalProps['loading']>},
        okText: {type: [String, Function] as PropType<ModalProps['okText']>},
        okButtonType: {type: String as PropType<ModalProps['okButtonType']>},
        cancleButtonType: {type: String as PropType<ModalProps['cancleButtonType']>},
        cancleText: {type: [String, Function] as PropType<ModalProps['cancleText']>},
        defaultPosition: {type: Object as PropType<ModalProps['defaultPosition']>},
        mask: {type: Boolean as PropType<ModalProps['mask']>, default: true},
        maskClosable: {type: Boolean as PropType<ModalProps['maskClosable']>, default: true},
        resetPostion: {type: Boolean as PropType<ModalProps['resetPostion']>, default: false},
        hasCloseIcon: {type: Boolean as PropType<ModalProps['hasCloseIcon']>},
        fullScreen: {type: Boolean as PropType<ModalProps['fullScreen']>},
        modelValue: {type: Boolean as PropType<ModalProps['modelValue']>},
        destroyOnClose: {type: Boolean as PropType<ModalProps['destroyOnClose']>},
        onOk: {type: Function as PropType<ModalProps['onOk']>},
        onCancel: {type: Function as PropType<ModalProps['onCancel']>},
    },
    emits: ['update:modelValue', 'closed', 'clickClose'],
    setup (props, {emit, slots}) {
        const wrap = ref();
        let maskEle: any;
        let draggable: any;
        const visible = ref(props.modelValue);
        const loading = ref(false);
        const opened = ref(visible.value);
        const destroyed = ref(props.destroyOnClose && !opened.value);
        let setOverflow = false;
        let originOverflow = '';
        const footerAlign = props.footerAlign ?? 'end';
        const footerReverse = props.footerReverse ?? false;
        const okButtonType = props.okButtonType ?? 'primary';
        const cancleButtonType = props.cancleButtonType ?? 'default';
        const zindex = usezIndex();

        const id = 'cm-modal-portal';
        const footer = props.footer ?? true;
        const modalId = createUniqueId();
        const okText = props.okText || '确 定';
        const cancleText = props.cancleText || '取 消';
        const mask = props.mask ?? true;
        const maskClosable = props.maskClosable ?? true;
        const hasCloseIcon = props.hasCloseIcon ?? true;
        const resetPostion = props.resetPostion ?? false;

        watch(() => props.modelValue, (val) => {
            visible.value = val;
        });

        watch(visible, (val) => {
            emit('update:modelValue', val);
        });

        const wrapClass = computed(() => ({
            'cm-modal-wrap': true,
            'cm-modal-fullscreen': props.fullScreen,
        }));

        const maskClass = computed(() => ({
            'cm-modal-mask': true,
            'cm-modal-mask-visible': visible.value
        }));

        const onClickClose = () => {
            emit('clickClose');
            onClose();
        };

        const onClose = () => {
            emit('closed');
            visible.value = false;
        };

        const onCancel = () => {
            onClose();
            props.onCancel?.();
        };

        const onOk = async () => {
            // 先判断显示loading
            if (props.loading) {
                if (!loading.value) {
                    loading.value = true;
                }
            }
            // 如果存在onOK回调判断是否返回值
            if (props.onOk) {
                const ret = await props.onOk?.();
                // 没有返回值并且非loading直接关闭
                if (ret === undefined && !loading.value) {
                    onClose();
                }
                // 返回true直接关闭
                if (ret === true) {
                    onClose();
                }
                // 返回false，重置loading
                if (ret === false) {
                    loading.value = false;
                }
            } else {
                if (!loading.value) {
                    onClose();
                }
            }
        };

        watchEffect(() => {
            const show = opened.value;
            const resetPostion = props.resetPostion;
            if (!show) {
                loading.value = false;
                if (setOverflow) {
                    document.body.style.overflow = originOverflow;
                    setOverflow = false;
                }
            } else {
                if (wrap.value) {
                    const wrapH = wrap.value.getBoundingClientRect().height;
                    const contentH = wrap.value.children[0].getBoundingClientRect().height;
                    // 超出显示内容
                    if (contentH > wrapH) {
                        wrap.value.style.overflow = 'auto';
                        wrap.value.children[0].style.top = 0;
                        const bodyStyle = window.getComputedStyle(document.body, null);
                        originOverflow = bodyStyle.overflow;
                        if (originOverflow !== 'hidden') {
                            document.body.style.overflow = 'hidden';
                            setOverflow = true;
                        }
                    } else {
                        wrap.value.style.overflow = 'none';
                        setOverflow = false;
                    }
                    setTimeout(() => {
                        wrap.value.focus();
                    }, 300);
                }
                if (resetPostion) {
                    if (draggable) {
                        draggable.reset();
                    }
                }
            }
        });

        const transition = useTransition({
            el: () => wrap.value,
            startClass: 'cm-modal-visible',
            activeClass: 'cm-modal-open',
            enterEndClass: 'cm-modal-opened',
            onLeave: () => {
                opened.value = false;
                props.destroyOnClose && (destroyed.value = true);
            },
            onEnter: () => {
                opened.value = true;
            }
        });

        watchEffect(() => {
            const v = visible.value;
            if (v) {
                props.destroyOnClose && (destroyed.value = false);
                transition.enter();
            } else {
                transition.leave();
            }
        });

        const onMaskClick = (e: any) => {
            if (maskClosable) {
                if (e.target === maskEle) {
                    visible.value = false;
                }
            }
        };

        const onkeydown = (e: any) => {
            if (e.keyCode === 27) {
                visible.value = false;
            }
        };

        return () => <Teleport to={usePortal(id, id)}>
            {
                mask ?
                    <div class={maskClass.value} onClick={onMaskClick} ref={(el) => maskEle = el} style={{"z-index": (zindex - 1)}}></div>
                    : null
            }
            <div class={wrapClass.value} ref={wrap} tab-index="1" onKeydown={onkeydown} style={{"z-index": zindex}}>
                <Draggable ref={(el) => draggable = el} bounds={props.bounds || 'body'} style={props.defaultPosition} handle={'.cm-modal-header[data-id="' + modalId + '"]'}
                    disabled={props.disabled}>
                    <div class="cm-modal" style={props.style}>
                        <div class="cm-modal-header" style={props.headerStyle} data-id={`${modalId}`}>
                            { props.title ? <div class="cm-modal-title">{props.title}</div> : null }
                            {
                                hasCloseIcon ? <span class="cm-modal-close" onClick={onClickClose}><FeatherX /></span> : null
                            }
                        </div>
                        <div class="cm-modal-body" style={props.bodyStyle}>
                            {destroyed.value ? null : slots.default?.()}
                        </div>
                        {
                            footer ?
                                <div class={{
                                    'cm-modal-footer': true,
                                    'cm-modal-footer-reverse': footerReverse,
                                    [`cm-modal-footer-${footerAlign}`]: !!footerAlign
                                }}>
                                    <Button type={okButtonType} loading={loading.value} onClick={onOk}>{okText}</Button>
                                    <Button type={cancleButtonType} onClick={onCancel}>{cancleText}</Button>
                                </div>
                                : null
                        }
                    </div>
                </Draggable>
            </div>
        </Teleport>;
    },
});

export default Modal;
export interface ModalConfig extends ModalProps{
    content?: any,
    status?: 'success'|'info'|'warning'|'error'|'confirm'
}

function ModalFun () {
    const visible = ref(true);
    let app;
    return {
        open (config: ModalConfig) {
            visible.value = true;
            const status = config.status;
            const icon = () => icons[status as any] ? createVNode(icons[status!], {
                class: `cm-modal-icon-${status}`,
                size: 24
            }) : null;
            const close = (v: boolean) => {
                visible.value = false;
                setTimeout(() => {
                    app.unmount();
                    app = null;
                }, 250);
            };
            config.style = {'min-width': '24vw', ...config.style};
            config.defaultPosition = {top: '200px', ...config.defaultPosition};
            delete config.status;

            const ele = usePortal('cm-modal-portal-instance', 'cm-modal-portal');
            if (!app) {
                app = createApp(() => <Modal {...config} onClosed={close} v-model={visible.value} class="cm-modal-instance">
                    {
                        icons[status as any]
                            ? <div class="cm-modal-left">
                                <div class="cm-modal-icon">
                                    {icon()}
                                </div>
                            </div>
                            : null
                    }
                    <div class="cm-modal-right">
                        {typeof config.content === 'function' ? config.content() : config.content}
                    </div>
                </Modal>);
                app.mount(ele);
            } else {
                app.unmount();
            }
        },
        success (config: ModalConfig) {
            config.status = 'success';
            return this.open(config);
        },
        info (config: ModalConfig) {
            config.status = 'info';
            return this.open(config);
        },
        warning (config: ModalConfig) {
            config.status = 'warning';
            return this.open(config);
        },
        error (config: ModalConfig) {
            config.status = 'error';
            return this.open(config);
        },
        confirm (config: ModalConfig) {
            config.status = 'confirm';
            return this.open(config);
        },
        remove () {
            visible.value = false;
            setTimeout(() => {
                if (app) {
                    app.unmount();
                }
            }, 250);
        }
    };
}

export const modal = ModalFun();
